🎖️GitЯра🎖️
Commit 7114dd3d5bd5eee9eb0a5d4f4ac4c87a0a724972
Parents : 0de099d
Author : James Rich <2199651+jamesarich@users.noreply.github.com>
Signature : Signature validation error
Date : 2026-08-13T18:53:04Z
Committer : GitHub <noreply@github.com>
Date : 2026-08-13T18:53:04Z
refactor(compose): adopt keyed SideEffect and drop redundant config write-back effects (#6684)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Changes
9 files changed, 17 insertions(+), 20 deletions(-)
Diff
diff --git a/androidApp/src/fdroid/kotlin/org/meshtastic/app/map/FdroidMapViewProvider.kt b/androidApp/src/fdroid/kotlin/org/meshtastic/app/map/FdroidMapViewProvider.kt
index 061cfe6305..9c93f9d1ed 100644
--- a/androidApp/src/fdroid/kotlin/org/meshtastic/app/map/FdroidMapViewProvider.kt
+++ b/androidApp/src/fdroid/kotlin/org/meshtastic/app/map/FdroidMapViewProvider.kt
@@ -17,7 +17,7 @@
package org.meshtastic.app.map
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Modifier
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.annotation.Single
@@ -34,8 +34,8 @@ class FdroidMapViewProvider : MapViewProvider {
sitePlannerNodeNum: Int?,
) {
val mapViewModel: MapViewModel = koinViewModel()
- LaunchedEffect(waypointId) { mapViewModel.setWaypointId(waypointId) }
- LaunchedEffect(sitePlannerNodeNum) { mapViewModel.setSitePlannerNodeNum(sitePlannerNodeNum) }
+ SideEffect(waypointId) { mapViewModel.setWaypointId(waypointId) }
+ SideEffect(sitePlannerNodeNum) { mapViewModel.setSitePlannerNodeNum(sitePlannerNodeNum) }
org.meshtastic.app.map.MapView(
modifier = modifier,
mapViewModel = mapViewModel,
diff --git a/androidApp/src/google/kotlin/org/meshtastic/app/map/GoogleMapViewProvider.kt b/androidApp/src/google/kotlin/org/meshtastic/app/map/GoogleMapViewProvider.kt
index a6cb067110..4ede4c5b61 100644
--- a/androidApp/src/google/kotlin/org/meshtastic/app/map/GoogleMapViewProvider.kt
+++ b/androidApp/src/google/kotlin/org/meshtastic/app/map/GoogleMapViewProvider.kt
@@ -17,7 +17,7 @@
package org.meshtastic.app.map
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Modifier
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.annotation.Single
@@ -34,8 +34,8 @@ class GoogleMapViewProvider : MapViewProvider {
sitePlannerNodeNum: Int?,
) {
val mapViewModel: MapViewModel = koinViewModel()
- LaunchedEffect(waypointId) { mapViewModel.setWaypointId(waypointId) }
- LaunchedEffect(sitePlannerNodeNum) { mapViewModel.setSitePlannerNodeNum(sitePlannerNodeNum) }
+ SideEffect(waypointId) { mapViewModel.setWaypointId(waypointId) }
+ SideEffect(sitePlannerNodeNum) { mapViewModel.setSitePlannerNodeNum(sitePlannerNodeNum) }
org.meshtastic.app.map.MapView(
modifier = modifier,
mapViewModel = mapViewModel,
diff --git a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt
index 48afb5ff74..c0aa305cde 100644
--- a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt
+++ b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt
@@ -49,6 +49,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -192,7 +193,7 @@ fun MessageScreen(
}
// Prevent the message TextField from stealing focus when the screen opens
- LaunchedEffect(contactKey) { focusManager.clearFocus() }
+ SideEffect(contactKey) { focusManager.clearFocus() }
// Derived state, memoized for performance
val channelInfo =
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreens.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreens.kt
index 251059a58c..e684aeeb91 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreens.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreens.kt
@@ -25,6 +25,7 @@ import androidx.compose.material3.SheetValue
import androidx.compose.material3.rememberBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -68,7 +69,7 @@ fun NodeDetailScreen(
onNavigateUp: () -> Unit = {},
compassViewModel: CompassViewModel? = null,
) {
- LaunchedEffect(nodeId) { viewModel.start(nodeId) }
+ SideEffect(nodeId) { viewModel.start(nodeId) }
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
LaunchedEffect(viewModel) { viewModel.navigationEvents.collect { onNavigate(it) } }
NodeDetailScaffold(
diff --git a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt
index e48bca9b45..794a85a23b 100644
--- a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt
+++ b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt
@@ -43,6 +43,7 @@ import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
@@ -115,7 +116,7 @@ fun DebugScreen(onNavigateUp: () -> Unit, viewModel: DebugViewModel) {
}
val filteredLogs = filteredLogsState
- LaunchedEffect(filteredLogs) { viewModel.updateFilteredLogs(filteredLogs) }
+ SideEffect(filteredLogs) { viewModel.updateFilteredLogs(filteredLogs) }
val shouldAutoScroll by remember { derivedStateOf { listState.firstVisibleItemIndex < 3 } }
if (shouldAutoScroll) {
diff --git a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseScreen.kt b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseScreen.kt
index 1ade81b114..3ec9725a24 100644
--- a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseScreen.kt
+++ b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseScreen.kt
@@ -32,7 +32,7 @@ import androidx.compose.material3.Slider
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -60,7 +60,7 @@ fun CleanNodeDatabaseScreen(viewModel: CleanNodeDatabaseViewModel, onBack: () ->
val onlyUnknownNodes by viewModel.onlyUnknownNodes.collectAsStateWithLifecycle()
val nodesToDelete by viewModel.nodesToDelete.collectAsStateWithLifecycle()
- LaunchedEffect(olderThanDays, onlyUnknownNodes) { viewModel.getNodesToDelete() }
+ SideEffect(olderThanDays, onlyUnknownNodes) { viewModel.getNodesToDelete() }
Scaffold(
topBar = {
diff --git a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreference.kt b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreference.kt
index 116e6d88f7..2ba9afa539 100644
--- a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreference.kt
+++ b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreference.kt
@@ -26,7 +26,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -71,8 +70,9 @@ fun MapReportingPreference(
enabled: Boolean,
) {
Column {
- var showMapReportingWarning by rememberSaveable { mutableStateOf(mapReportingEnabled) }
- LaunchedEffect(mapReportingEnabled) { showMapReportingWarning = mapReportingEnabled }
+ // Tracks the switch locally so the consent card can show before the config round-trips; re-keyed on
+ // [mapReportingEnabled] so an externally-changed config wins over a stale local toggle.
+ var showMapReportingWarning by rememberSaveable(mapReportingEnabled) { mutableStateOf(mapReportingEnabled) }
SwitchPreference(
title = stringResource(Res.string.map_reporting),
summary = stringResource(Res.string.map_reporting_summary),
diff --git a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/StatusMessageConfigItemList.kt b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/StatusMessageConfigItemList.kt
index 4f97cc8bf0..e474778e33 100644
--- a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/StatusMessageConfigItemList.kt
+++ b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/StatusMessageConfigItemList.kt
@@ -21,7 +21,6 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalFocusManager
@@ -61,8 +60,6 @@ fun StatusMessageConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Uni
val formState = rememberConfigState(initialValue = statusMessageConfig)
val focusManager = LocalFocusManager.current
- LaunchedEffect(statusMessageConfig) { formState.value = statusMessageConfig }
-
RadioConfigScreenList(
rebootBehavior = RebootBehavior.NEVER,
title = stringResource(Res.string.status_message),
diff --git a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/TAKConfigItemList.kt b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/TAKConfigItemList.kt
index ffcc450e7b..cc18c69a0e 100644
--- a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/TAKConfigItemList.kt
+++ b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/TAKConfigItemList.kt
@@ -38,7 +38,6 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -115,8 +114,6 @@ fun TAKConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
val takConfig = state.moduleConfig.tak ?: ModuleConfig.TAKConfig()
val formState = rememberConfigState(initialValue = takConfig)
- LaunchedEffect(takConfig) { formState.value = takConfig }
-
val effectiveResponseState =
when (state.responseState) {
is ResponseState.Loading -> ResponseState.Empty
Served by rngit 1.5.2 - Generated in 0.14s